package com.tbit.uqbike.client.controller;


import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.tbit.uqbike.client.aop.charge.ChargeReturnBikeLimit;
import com.tbit.uqbike.client.aop.charge.RechargeTypeParam;
import com.tbit.uqbike.client.aop.charge.TokenParam;
import com.tbit.uqbike.client.config.RefundSwitchConfig;
import com.tbit.uqbike.client.constant.RechargeConstant;
import com.tbit.uqbike.client.constant.RedisConstant;
import com.tbit.uqbike.client.constant.ResponseCode;
import com.tbit.uqbike.client.constant.SystemConstant;
import com.tbit.uqbike.client.constant.annotation.PreventDuplication;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.newEntity.WeixinPushModelVo;
import com.tbit.uqbike.client.remote.WeixinPay;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.util.*;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.refund.model.Amount;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 微信支付
 *
 * @author Leon
 * 2014-4-10 上午09:35:42
 */
@Controller
@RequestMapping("/weixinPay")
@Slf4j
public class WeixinPayController {
    @Autowired
    private WeixinPayService weixinPayService;
    @Autowired
    private RechargeService rechargeService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private WeixinPayConfigService weixinPayConfigService;
    @Autowired
    private MQProducer mqProducer;
    @Resource
    private RefundSwitchConfig refundSwitchConfig;

    /* 微信异步通知 */
    @SuppressWarnings("rawtypes")
    @RequestMapping("/notify")
    public void notify( HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 1. 设置请求编码，防止中文乱码
        request.setCharacterEncoding(StandardCharsets.UTF_8.name());
        // 2. 读取请求体
        String body = readRequestBody(request);
        if (!StringUtils.hasText(body)) {
            log.info("请求体为空，跳过回调处理");
            return;
        }
        log.info("请求体" + body);

        // 根据请求头中的 content-type -> text/xml 或 application/json 区分是V2回调还是V3回调
        String contentType = request.getHeader("content-type");
        if (Objects.equals(contentType,"application/json")){
            WeixinNotifyV3(request, response, body);
        }else {
            WeixinNotify(request, response, body);
        }
    }

    private String readRequestBody(HttpServletRequest request) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        return baos.toString(StandardCharsets.UTF_8.name());
    }

    /**
     * 微信V3 回调解析
     * @param request 请求
     * @param response 响应
     * @param body 应答报文主体
     */
    private void WeixinNotifyV3(HttpServletRequest request, HttpServletResponse response, String body) throws Exception {
        log.info("进入v3微信回调");
        // 将回调请求体内容转换为对象
        JSONObject bodyObject = JSON.parseObject(body);
        String eventType = bodyObject.getString("event_type");

        String wechatPaySerial = request.getHeader("wechatpay-serial");
        String wechatpayNonce = request.getHeader("wechatpay-nonce");
        String wechatSignature = request.getHeader("wechatpay-signature");
        String wechatTimestamp = request.getHeader("wechatpay-timestamp");

        // 构造 RequestParam
        com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(wechatpayNonce)
                .signature(wechatSignature)
                .timestamp(wechatTimestamp)
                .body(body)
                .build();

        // 回调处理
        boolean isSuccess = false;
        if (eventType.startsWith("TRANSACTION")){
            Transaction transaction = getDecryptObject(requestParam, Transaction.class);
            if (Objects.nonNull(transaction)){
                String transactionId = transaction.getTransactionId();
                String orderNO = transaction.getOutTradeNo();
                String tradeState = transaction.getTradeState().toString();
                Integer orderMoney = transaction.getAmount().getTotal();
                Integer payMoney = transaction.getAmount().getPayerTotal();
                String date = transaction.getSuccessTime();
                if (Objects.nonNull(date)){
                    OffsetDateTime offsetDateTime = OffsetDateTime.parse(date);
                    date = offsetDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                }
                isSuccess = dealPayCallBack(transactionId,orderNO,tradeState,orderMoney,payMoney,date);
            }
        }else if (eventType.startsWith("REFUND")){
            RefundNotification refundNotification = getDecryptObject(requestParam, RefundNotification.class);
            if (Objects.nonNull(refundNotification)){
                String transactionId = refundNotification.getTransactionId();
                String orderNO = refundNotification.getOutTradeNo();
                String refundNO = refundNotification.getOutRefundNo();
                String refundStatus = refundNotification.getRefundStatus().toString();
                String date = refundNotification.getSuccessTime();
                if (Objects.nonNull(date)){
                    OffsetDateTime offsetDateTime = OffsetDateTime.parse(date);
                    date = offsetDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                }
                Amount amount = refundNotification.getAmount();
                Integer orderMoney = amount.getTotal().intValue();
                Integer refundMoney = amount.getRefund().intValue();
                isSuccess = dealRefundCallBack(transactionId,orderNO,refundNO,refundStatus,date,orderMoney,refundMoney);
            }
        }else {
            log.warn("未知的微信V3回调");
            return ;
        }

        // 回复微信的回调通知
        int code = isSuccess ? HttpStatus.OK.value() : HttpStatus.INTERNAL_SERVER_ERROR.value();
        Map<String,String> resMap = new HashMap<>();
        if (isSuccess){
            resMap.put("code","SUCCESS");
            resMap.put("message","成功");
        }else {
            resMap.put("code","FAIL");
            resMap.put("message","失败");
        }
        response.setStatus(code);response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        out.write(JSON.toJSONString(resMap).getBytes());
        out.flush();
        out.close();
    }

    /**
     * 获取微信支付回调内容
     * @param requestParam 微信V3 请求体
     * @param target 目标类（支付 Transaction ，退款 RefundNotification）
     * @return 微信支付回调内容
     */
    @Nullable
    private <T> T getDecryptObject(com.wechat.pay.java.core.notification.RequestParam requestParam, Class<T> target) {
        T transaction = null;
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig) WeixinPay.config);
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            transaction = parser.parse(requestParam, target);
            log.info("微信V3回调解密内容{}",JSON.toJSONString(transaction));
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return null;
        }
        return transaction;
    }

    /**
     * 退款回调处理含描述
     * @param transactionId 微信支付系统生成的订单号 transaction_id
     * @param orderNO 业务订单号 ， 商户系统内部订单号 out_trade_no
     * @param refundNO 退款单号 ， 商户系统内部退款单号 out_refund_no
     * @param refundStatus 退款状态 refund_status
     * @param date 退款成功时间 success_time
     * @param orderMoney 订单金额， 订单总金额，单位为分 total
     * @param refundMoney 退款金额， 退款总金额，单位为分 refund
     * @return 回调是否成功（支付成功/退款成功）
     * @throws Exception  异常
     */
    private boolean dealRefundCallBack(String transactionId, String orderNO, String refundNO, String refundStatus, String date, Integer orderMoney, Integer refundMoney) throws Exception {
        if(!Objects.equals(refundStatus,"SUCCESS")){
            return false;
        }
        String refundModel = refundSwitchConfig.getRefundModel();
        if (Objects.equals(refundModel,RefundSwitchConfig.REFUND_NEW)){
            weixinPayService.refundCallbackTest(orderNO, refundNO, refundStatus, date,orderMoney.doubleValue(),refundMoney.doubleValue(),transactionId);
        }else {
            weixinPayService.refundCallback(orderNO, refundNO, refundStatus, date);
        }
        return true;
    }

    /**
     * 支付回调处理函数
     * @param transactionId 微信支付系统生成的订单号 transaction_id
     * @param orderNO 业务订单号 ， 商户系统内部订单号 out_trade_no
     * @param tradeState  支付状态 trade_state @see Transaction.TradeStateEnum
     * @param orderMoney 订单金额， 订单总金额，单位为分 total
     * @param payMoney 用户支付金额，单位为分 payer_total
     * @param date success_time 支付完成时间 需要格式化为 "yyyy-MM-dd HH:mm:ss"
     * @return 回调是否成功（支付成功/退款成功）
     */
    private boolean dealPayCallBack(String transactionId ,String orderNO, String tradeState, Integer orderMoney, Integer payMoney ,String date) {
        //todo ACCEPT状态文档未描述，暂时未知属于哪种情况
        if (!Objects.equals(tradeState, Transaction.TradeStateEnum.SUCCESS.toString())){
            return false;
        }
        // 查询订单
        Recharge recharge = rechargeService.getByOrderNO(orderNO);
        if (recharge != null && recharge.getState() == 0) {
            // 订单金额
            int rechargeMoney = recharge.getMoney().intValue();
            log.info("微信订单,订单号=" + orderNO + ",订单金额=" + orderMoney/100d + "元,现金支付金额=" + payMoney/100D + "元");
            if (rechargeMoney != orderMoney){
                log.error("订单金额与支付金额不一致,订单号=" + orderNO + ",订单金额=" + orderMoney/100d + "元,现金支付金额=" + payMoney/100D + "元");
            }else {
                mqProducer.sendPay(orderNO,orderNO,transactionId,date,null);
            }
        } else {
            log.error("没有查询到支付订单，订单号=" + orderNO);
        }
        return true;
    }

    /**
     * 微信v2版本异步通知（支付/退款）
     * @param request
     * @param response
     */
    private void WeixinNotify(HttpServletRequest request, HttpServletResponse response, String body) {
        try {
            log.info("进入v2微信回调");
            String resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";

            Map m = WeixinPayUtil.parseXmlToList2(body);
            log.info("微信回调=" + m);
            WxPayResult wpr = new WxPayResult();
            if (m.get("appid") != null) {
                wpr.setAppid(m.get("appid").toString());
            }
            if (m.get("bank_type") != null) {
                wpr.setBankType(m.get("bank_type").toString());
            }
            if (m.get("cash_fee") != null) {
                wpr.setCashFee(m.get("cash_fee").toString());
            }
            if (m.get("fee_type") != null) {
                wpr.setFeeType(m.get("fee_type").toString());
            }
            if (m.get("is_subscribe") != null) {
                wpr.setIsSubscribe(m.get("is_subscribe").toString());
            }
            if (m.get("mch_id") != null) {
                wpr.setMchId(m.get("mch_id").toString());
            }
            if (m.get("nonce_str") != null) {
                wpr.setNonceStr(m.get("nonce_str").toString());
            }
            if (m.get("openid") != null) {
                wpr.setOpenid(m.get("openid").toString());
            }
            if (m.get("out_trade_no") != null) {
                wpr.setOutTradeNo(m.get("out_trade_no").toString());
            }
            if (m.get("result_code") != null) {
                wpr.setResultCode(m.get("result_code").toString());
            }
            if (m.get("return_code") != null) {
                wpr.setReturnCode(m.get("return_code").toString());
            }
            if (m.get("sign") != null) {
                wpr.setSign(m.get("sign").toString());
            }
            if (m.get("time_end") != null) {
                wpr.setTimeEnd(m.get("time_end").toString());
            }
            if (m.get("total_fee") != null) {
                wpr.setTotalFee(m.get("total_fee").toString());
            }
            if (m.get("trade_type") != null) {
                wpr.setTradeType(m.get("trade_type").toString());
            }
            if (m.get("transaction_id") != null) {
                wpr.setTransactionId(m.get("transaction_id").toString());
            }
            if (m.get("coupon_fee") != null) {
                wpr.setCoupon_fee(m.get("coupon_fee").toString());
            }

            log.info("transaction_id=" + m.get("transaction_id") + ",out_trade_no=" + m.get("out_trade_no") + ",cash_fee=" + m.get("cash_fee"));
            if ("SUCCESS".equals(wpr.getResultCode())) {
                //WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAppid(wpr.getAppid());
                // 支付从品牌拿配置
                WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByPartnerAndAppId(wpr.getMchId(), wpr.getAppid(),true);
                if (weixinPayConfig == null) {
                    weixinPayConfig = weixinPayConfigService.getByAppid(wpr.getAppid());
                }
                if (weixinPayConfig == null) {
                    throw new BaseException("-2006", "该品牌微信支付未配置");
                }
                log.info("解析支付回调使用的微信支付配置为{}", JSON.toJSONString(weixinPayConfig));
                /**签名校验*/
                if (!WXPayUtil.isSignatureValid(body, weixinPayConfig.getPartnerkey())) {
//                    log.info("签名检验失败" + weixinPayConfig.getPartnerkey());
                    throw new BaseException("-2010", "签名校验失败");
                }

                // 支付成功
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

                /** 查询订单 */
                Recharge recharge = rechargeService.getByOrderNO(wpr.getOutTradeNo());
                if (recharge != null && recharge.getState() == 0) {
                    /**查询金额是否和订单一致*/
                    Double rechargeMondey = recharge.getMoney() / 100;
                    /**微信总金额=代金券金额+现金支付金额*/
					/*Double weixinMoney=Double.valueOf(wpr.getCashFee())/100D;
					if(wpr.getCoupon_fee()!=null){
						Double couponFee=Double.valueOf(wpr.getCoupon_fee())/100D;
						weixinMoney= DoubleUtil.format(weixinMoney+couponFee);
						log.info("代金券,订单号="+wpr.getOutTradeNo()+"代金券金额="+couponFee+",总金额="+weixinMoney);
					}*/
                    Double weixinMoney = Double.valueOf(wpr.getTotalFee()) / 100D;
                    log.info("微信订单,订单号=" + wpr.getOutTradeNo() + ",订单金额=" + rechargeMondey + "元,现金支付金额=" + Double.valueOf(wpr.getCashFee()) / 100D + "元");

                    if (!rechargeMondey.equals(weixinMoney)) {
                        log.error("订单金额与支付金额不一致,订单号=" + wpr.getOutTradeNo() + ",订单金额=" + rechargeMondey + "元,现金支付金额=" + Double.valueOf(wpr.getCashFee()) / 100D + "元");
                    } else {
                        Date date = DateUtil.parse(wpr.getTimeEnd());
                        String dateTime = DateUtil.formatDateTime(date);
                        mqProducer.sendPay(wpr.getOutTradeNo(), wpr.getOutTradeNo(), wpr.getTransactionId(), dateTime, null);
                    }

                } else {
                    log.error("没有查询到支付订单，订单号=" + wpr.getOutTradeNo());
                }

                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            }
            if ("SUCCESS".equals(wpr.getReturnCode())) {
                //WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAppid(wpr.getAppid());
                // 退款从数据库拿配置
                WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByPartnerAndAppId(wpr.getMchId(), wpr.getAppid(),false);
                if (weixinPayConfig == null) {
                    weixinPayConfig = weixinPayConfigService.getByAppid(wpr.getAppid());
                }
                log.info("解析退款回调使用的微信支付配置为{}", JSON.toJSONString(weixinPayConfig));
                /**区分退款以及充值 退款才有req_info*/
                if (m.get("req_info") != null) {
                    //解密
                    String reqInfo = WXPayUtil.decryptData(m.get("req_info").toString(), weixinPayConfig.getPartnerkey());
                    log.info("微信退款结果{}",reqInfo);
                    Map<String, String> map = WXPayUtil.xmlToMap(reqInfo);// 转换成map
                    if (null == map || map.size() == 0) {
                        log.info("解密异常");
                    }
                    //支付单号 商户订单号
                    String outTradeNo = map.get("out_trade_no");
                    //退款单号 商户退款单号
                    String outRefundNo = map.get("out_refund_no");
                    //退款状态
                    String refundStatus = map.get("refund_status");
                    //成功时间
                    String successTime = map.get("success_time");

                    String refundModel = refundSwitchConfig.getRefundModel();
                    if (Objects.equals(refundModel,RefundSwitchConfig.REFUND_NEW)){
                        //退款总金额 订单金额
                        String totalRefundMoney = map.get("total_fee");
                        //本次退款金额 申请退款金额
                        String refundMoney = map.get("refund_fee");
                        // 微信订单号
                        String transactionId = map.get("transaction_id");
                        weixinPayService.refundCallbackTest(outTradeNo, outRefundNo, refundStatus, successTime,Double.parseDouble(totalRefundMoney),Double.parseDouble(refundMoney),transactionId);
                    }else {
                        weixinPayService.refundCallback(outTradeNo, outRefundNo, refundStatus, successTime);
                    }

                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

                }
                BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
                out.write(resXml.getBytes());
                out.flush();
                out.close();
            }
        } catch (Exception e) {
            log.error("微信回调异常", e);
        }
    }


    /**
     * 创建微信APP支付订单
     *
     * @param token        令牌
     * @param adAccountId  区域id
     * @param money        金额
     * @param remark       备注
     * @param deposit      是否押金
     * @param rechargeType 充值类型
     * @param dispatchType 调度类型
     * @param response     响应
     * @ignoreParams request
     */
    @RequestMapping("/createOrder_app")
    @PreventDuplication(expireSeconds = 5)
    @ChargeReturnBikeLimit
    public void createOrder_app(@TokenParam String token, Integer adAccountId, Double money, String remark, Boolean deposit, @RequestParam(defaultValue = "0") @RechargeTypeParam Integer rechargeType, Integer dispatchType,
                                HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {

            if (deposit == null) {
                deposit = false;
            }
            User user = tokenService.getUser(token);
            if (deposit) {
                Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            }
            if (!deposit) {
                /* 校验 */
                Assert.notNull(money, "-70001", "充值金额不能为空");
                if (adAccountId != null) {
                    redisService.add(user.getUserId() + ".agentId", String.valueOf(adAccountId), 600);
                }

            }
            /**充值结束订单需要校验下用户点击还车检验到创建订单时长，超出1分钟不许还车*/
            if (rechargeType.equals(RechargeConstant.recharge_returnBike)) {
                String no = redisService.get(RedisConstant.USER_FINISH_PAY + user.getUserId());
                if (no == null) {
                    throw new BaseException("-20018", "订单超时，请重试");
                }
            }
            /**存储调度类型还车*/
            if (dispatchType != null) {
                redisService.add(RedisConstant.REDIS_ACCOUNT_USER_DISPATCH + user.getUserId(), String.valueOf(dispatchType), 90);
            }


            WeixinPayParam wpp = weixinPayService.getOrder_app(user.getAccountId(), user.getUserId(), money, remark, deposit, adAccountId, rechargeType);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, wpp);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建微信小程序支付订单
     *
     * @param token        令牌
     * @param adAccountId  区域id
     * @param code         code
     * @param money        金额
     * @param remark       备注
     * @param deposit      是否押金
     * @param rechargeType 充值类型
     * @param dispatchType 调度类型
     * @param response     响应
     * @ignoreParams request
     */
    @RequestMapping("/createOrder_weixin")
    @PreventDuplication(expireSeconds = 5)
    @ChargeReturnBikeLimit
    public void createOrder_weixin(@TokenParam String token, Integer adAccountId, String code, Double money, String remark, Boolean deposit,@RechargeTypeParam Integer rechargeType, Integer dispatchType,
                                   HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);

            Assert.hasLength(code, "-3027", "微信code不能为空");

            if (deposit == null) {
                deposit = false;
            }
            if (deposit) {
                Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            }
            if (!deposit) {
                /* 校验 */
                Assert.notNull(money, "-70001", "充值金额不能为空");
                if (adAccountId != null) {
                    redisService.add(user.getUserId() + ".agentId", String.valueOf(adAccountId), 600);
                }

            }

            if (rechargeType == null) {
                rechargeType = 0;
            }

            /**充值结束订单需要校验下用户点击还车检验到创建订单时长，超出1分钟不许还车*/
            if (rechargeType.equals(RechargeConstant.recharge_returnBike)) {
                String no = redisService.get(RedisConstant.USER_FINISH_PAY + user.getUserId());
                if (no == null) {
                    throw new BaseException("-20018", "订单超时，请重试");
                }
            }

            /**存储调度类型还车*/
            if (dispatchType != null) {
                redisService.add(RedisConstant.REDIS_ACCOUNT_USER_DISPATCH + user.getUserId(), String.valueOf(dispatchType), 90);
            }


            String result = weixinPayService.getOrder_weixin(code, user.getAccountId(), user.getUserId(), money, remark, deposit, adAccountId, rechargeType);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建免押会员订单_小程序
     *
     * @param token       令牌
     * @param code        代码
     * @param adAccountId 区域id
     * @param memberFeeId 免押会员id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createMemberOrder_weixin")
    @PreventDuplication(expireSeconds = 5)
    public void createMemberOrder_weixin(String token, String code, Integer adAccountId, Integer memberFeeId,
                                         HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.hasLength(code, "-3027", "微信code不能为空");
            Assert.notNull(memberFeeId, "-120004", "memberFeeId不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");

            /**放入redis*/
            redisService.add(user.getUserId() + ".agentId", String.valueOf(adAccountId), 600);
            String result = weixinPayService.getMemberOrder_weixin(code, user, memberFeeId, adAccountId);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建免押会员订单_APP
     *
     * @param token       令牌
     * @param memberFeeId 免押会员id
     * @param adAccountId 区域id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createMemberOrder_app")
    @PreventDuplication(expireSeconds = 5)
    public void createMemberOrder_app(String token, Integer memberFeeId, Integer adAccountId,
                                      HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.notNull(memberFeeId, "-120004", "memberFeeId不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            /**放入redis*/
            redisService.add(user.getUserId() + ".agentId", String.valueOf(adAccountId), 600);
            WeixinPayParam wpp = weixinPayService.getMemberOrder_app(user, memberFeeId, adAccountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, wpp);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建骑行会员订单_小程序
     *
     * @param token       令牌
     * @param code        代码
     * @param adAccountId 区域id
     * @param rideCardId  骑行卡id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createRideCardOrder_weixin")
    @PreventDuplication(expireSeconds = 5)
    public void createRideCardOrder_weixin(String token, String code, Integer adAccountId, Integer rideCardId,
                                           HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.hasLength(code, "-3027", "微信code不能为空");
            Assert.notNull(rideCardId, "-120001", "骑行会员卡id不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            String result = weixinPayService.getRideCardOrder_weixin(code, user, rideCardId, adAccountId);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建骑行会员订单_APP
     *
     * @param token       令牌
     * @param adAccountId 区域id
     * @param rideCardId  骑行卡id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createRideCardOrder_app")
    @PreventDuplication(expireSeconds = 5)
    public void createRideCardOrder_app(String token, Integer adAccountId, Integer rideCardId,
                                        HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.notNull(rideCardId, "-120001", "骑行会员卡id不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            WeixinPayParam wpp = weixinPayService.getRideCardOrder_app(user, rideCardId, adAccountId);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, wpp);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建会员卡订单_小程序
     *
     * @param token       令牌
     * @param code        代码
     * @param adAccountId 区域id
     * @param vipId       会员卡id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createVipOrder_weixin")
    @PreventDuplication(expireSeconds = 5)
    public void createVipOrder_weixin(String token, String code, Integer adAccountId, Integer vipId,
                                      HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.hasLength(code, "-3027", "微信code不能为空");
            Assert.notNull(vipId, "-150001", "vipId不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            String result = weixinPayService.getVipOrder_weixin(code, user, vipId, adAccountId);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建会员卡订单_APP
     *
     * @param token       令牌
     * @param vipId       会员卡id
     * @param adAccountId 区域id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createVipOrder_app")
    @PreventDuplication(expireSeconds = 5)
    public void createVipOrder_app(String token, Integer vipId, Integer adAccountId,
                                   HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.notNull(vipId, "-150001", "vipId不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");

            WeixinPayParam wpp = weixinPayService.getVipOrder_app(user, vipId, adAccountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, wpp);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建优惠券套餐订单_小程序
     *
     * @param token       令牌
     * @param code        代码
     * @param adAccountId 区域id
     * @param mealId      优惠券套餐id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createMeal_weixin")
    @PreventDuplication(expireSeconds = 5)
    public void createMeal_weixin(String token, String code, Integer adAccountId, Integer mealId,
                                  HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.hasLength(code, "-3027", "微信code不能为空");
            Assert.notNull(mealId, "-180001", "主键id不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");

            String result = weixinPayService.getMeal_weixin(code, user, mealId, adAccountId);

            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, result);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 创建优惠券套餐订单_APP
     *
     * @param token       令牌
     * @param mealId      优惠券套餐id
     * @param adAccountId 区域id
     * @param response    响应
     * @ignoreParams request
     */
    @RequestMapping("/createMeal_app")
    @PreventDuplication(expireSeconds = 5)
    public void createMeal_app(String token, Integer mealId, Integer adAccountId,
                               HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);
            Assert.notNull(mealId, "-180001", "主键id不能为空");
            Assert.notNull(adAccountId, "-10001", "区域id不能为空");
            WeixinPayParam wpp = weixinPayService.getMeal_app(user, mealId, adAccountId);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, wpp);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }


    /**
     * 获取小程序accesstoken
     *
     * @param accountId
     * @param request
     * @param response
     */
    @RequestMapping("/getAccessToken")
    public void getAccessToken(Integer accountId,
                               HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            checkSign(accountId, request);
            WeixinPayConfig weixinPayConfig = weixinPayConfigService.getByAccountId(accountId,
                    RechargeConstant.RECHARGE_WEIXIN_XCX);
            if (weixinPayConfig == null) {
                throw new BaseException("-2006", "该品牌微信支付未配置");
            }
            String accessToken = weixinPayService.getAccessToken(weixinPayConfig);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, accessToken);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 校验签名
     *
     * @param accountId
     * @param request
     */
    private void checkSign(Integer accountId, HttpServletRequest request) {
        String sign = request.getHeader("sign");
        String timestamp = request.getHeader("timestamp");
        Assert.notNull(sign, "-103", "无权操作");
        Assert.notNull(timestamp, "-103", "无权操作");
        if (!Objects.requireNonNull(MD5PasswordUtil.MD5(accountId + SystemConstant.SECRET + timestamp)).equalsIgnoreCase(sign)) {
            throw new BaseException("-103", "无权操作");
        }
    }

    /**
     * 其他端发送模板消息
     *
     * @param weixinPushModelVo 传递参数实体类
     * @param request
     * @param response
     */
    @RequestMapping("/sendLockMsg")
    public void sendLockMsg(@RequestBody WeixinPushModelVo weixinPushModelVo,
                            HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.notNull(weixinPushModelVo.getAccountId(), "-10001", "请传入品牌id");
            Assert.notNull(weixinPushModelVo.getUserId(), "-20005", "用户不存在");
            Assert.notNull(weixinPushModelVo.getParam(), "-3071", "附件参数不能为空");
            weixinPayService.sendLockMsg(weixinPushModelVo.getAccountId(), weixinPushModelVo.getUserId(), weixinPushModelVo.getParam(), weixinPushModelVo.getType(), null);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }

    /**
     * 欠费短信通知跳转小程序url
     *
     * @param accountId 品牌id
     * @param type      类型 17
     * @param request
     * @param response
     */
    @RequestMapping("/getUrlScheme")
    public void getUrlScheme(Integer accountId, @RequestParam(defaultValue = "17") Integer type,
                             HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.notNull(accountId, "-10001", "请传入品牌id");
            String openLink = weixinPayService.getUrlScheme(accountId, type);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, openLink);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }

        OutputUtil.renderText(request, response, map);
    }
}
